<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="application-name" content="JSON SCADA Events Viewer">
<meta name="description" content="Events Viewer">
<meta name="author" content="Ricardo L. Olsen">
<meta name="viewport" content="width=980, user-scalable=no" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="apple-touch-icon" href="images/chrono.png" />
<title>Events Viewer</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />    
<style>
@font-face {
  font-family: 'Source Sans Pro';
  font-style: normal;
  font-weight: 400;
  src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(lib/SourceSansPro-Regular.woff2) format('woff2');
}
</style>
<script src="util.js"></script>
<script src="lib/jquery-1.8.3.js"></script>
<script src="lib/core-1.0.js"></script>
<script src="lib/shortcut-2.01b.js"></script>
<script src="fan.js"></script>
<script src="legacy_options.js"></script>
<script src="../i18n/messages_i18n.js"></script>
<script src="config_viewers_default.js"></script>
<script src="../conf/config_viewers.js"></script>
<script src="images.js"></script>
<script src="opc-codes.js"></script>
<script>
"use strict";

// This Viewer shows Sequence Of Events (SOE) data from the SCADA System.

// {json:scada} - Copyright 2020-2023 - Ricardo L. Olsen
// Derived from: OSHMI/Open Substation HMI - Copyright 2008-2021 - Ricardo L. Olsen

var L = []; // Event list
var V = []; // Point values
var S = []; // Point values as String
var F = []; // Point quality flags
var T = []; // Alarm time tags
var TAGS = []; // Point tag names
var NPTS = []; // Point numbers by tags names
var SUBS = []; // Grouping level 1 of points (e.g. substation)
var BAYS = []; // Grouping level 2 of points (e.g. bay)
var DCRS = []; // Point description
var ANOTS = []; // Point blocking annotation
var DNOTES = []; // Point documental annotations
var STONS = []; // On status texts
var STOFS = []; // Off status texts
var LIMSUPS = []; // Analog superior limits for points
var LIMINFS = []; // Analog inferior limits for points
var NUM_VAR = 0;        // number of digital values changes on server
var NUM_VAR_ANT = 0;    // previous value of NUM_VAR
var ALARMBEEP = 0; // alarm beep status
var GROUP1_LIST = []; // list of possible group1 values
var BEEP_POINTKEY = -1;
var CNTUPDATES_POINTKEY = -2;

// for command dialog
var NPTO, ID, ESTACAO, MODULO, DESC, ST_ON, ST_OFF, VAL_STR, CNPTO, CID, CDESC, CST_ON, CST_OFF;
var LIMS, LIMI, HISTER, ALRIN, ANOT, VLNOR, ESTALM, UNIDADE, SIMULACAO = 0;
var ComandoAck = ''; // texto para confirmação do comando 

var cbf_F; // to receive a callback function name
// var WSCons; // write status of event list: 1=begin, 2=end

var WebSAGE =
{
g_nummaxevehist: EventsViewer_MaxHistoricalEvents, // max number of historical events
g_nummaxevetr: EventsViewer_MaxRealtimeEvents, // max number of realtime events
g_timeOutRefresh: 1000 * EventsViewer_RefreshTime, // tempo de refresh dos dados
g_timeOutReconhece: 4000, // refresh timeout after full ack/del of events
g_timeOutFalha: 30000,    // server failure timeout
g_timeOutServerStatus: 2000, // time to test server for status
g_toutID: 0,
g_timerID: 0,
g_timeoutFalhaID: 0,
g_ServerStatusID: 0,
g_pass: 0,
g_COL_DATA:   0,
g_COL_HORA:   1,
g_COL_MSEC:   2,
g_COL_NPONTO: 3,
g_COL_ID:     4,
g_COL_SUBEST: 5,
g_COL_DESCR:  6,
g_COL_EVENTO: 7,
g_COL_FLAGS:  8,
g_COL_QUALIF: 9,
g_COL_TIMEDIF: 10,
g_COL_EVENTID: 11,
g_bipa: 0,
g_nponto_sup: 0,
g_nponto_cmd: 0,
g_win_cmd: {},
g_win_1stdraw: 0,
g_wait_win: 0,
g_agreg_ant: false,
g_cbagreg_ant: false,
g_modo: 0,
g_waitingServer: 0,
g_fontsize: 16,  // font size on grid
g_tbl: {},
g_tblhead: {},
g_gpstime: 1, // 1=field GPS, 0=local time
g_titulo_janela: "",
g_filtro_se: "",
g_loadtime: 0,
g_infomod: {},
g_infomodtm: {},
g_infomodnpt: {},
g_infomodid: {},
g_infomodsubest: {},
g_subst_list: [],
g_travaInfo: 0,
g_split_line_code: "®æ",

doNothing: function() {},

// Return value from tag or number
getValue: function(tagornumber) {
  return V[tagornumber] || V[NPTS[tagornumber]] || 0;
},

// Return the string value from tag or number
getStringValue: function(tagornumber) {
  return S[tagornumber] || S[NPTS[tagornumber]] || "";
},

// Return flags from tag or number
getFlags: function(tagornumber) {
  var f = F[tagornumber] || F[NPTS[tagornumber]];
  if (isNaN(f))
    return 0xa0 | (WebSAGE.getValue(tagornumber) == 0 ? 0x02 : 0x01);
  else return f;
},

// Return inferior limit from tag or number
getInfLim: function(tagornumber) {
  return LIMINFS[tagornumber] || LIMINFS[NPTS[tagornumber]] || 0;
},

// Return superior limit from tag or number
getSupLim: function(tagornumber) {
  return LIMSUPS[tagornumber] || LIMSUPS[NPTS[tagornumber]] || 0;
},

// Return substation from tag or number
getSubstation: function(tagornumber) {
  return SUBS[tagornumber] || SUBS[NPTS[tagornumber]] || "";
},

// Return bay from tag or number
getBay: function(tagornumber) {
  return BAYS[tagornumber] || BAYS[NPTS[tagornumber]] || "";
},

// Return description from tag or number
getDescription: function(tagornumber) {
  return DCRS[tagornumber] || DCRS[NPTS[tagornumber]] || "";
},

// Return alarm time from tag or number
getTime: function(tagornumber) {
  return T[tagornumber] || T[NPTS[tagornumber]] || "";
},

// função auxiliar para escrever dados na janela de comando pelo id do objeto
writeElemByIdWnd: function(win, id, txt) {
  if ("$" in win)
    win.$("#" + id).text(txt);
},

// Return annotation
getAnnotation: function(tagornumber) {
return ANOTS[tagornumber] || ANOTS[NPTS[tagornumber]] || "";
},

// busca dados do servidor e prepara chamada temporizada de showValsCmd para
janelaInfo: function(nponto) {
  // faz um bloqueio de 1,5s
  if (WebSAGE.g_travaInfo) {
    return;
  }
  WebSAGE.g_travaInfo = 1;
  setTimeout("WebSAGE.g_travaInfo=0", 1500);

  if (nponto != 0) {
    WebSAGE.g_nponto_sup = nponto;
    WebSAGE.g_cntTentativaInfo = 3;
  } else {
    WebSAGE.g_cntTentativaInfo--;
  }

  if (WebSAGE.g_cntTentativaInfo <= 0) {
    return;
  }

  LIMS = 0;
  LIMI = 0;
  HISTER = 0;
  ALRIN = 0;
  NPTO = 0;
  ID = "";
  DESC = "";

  if (typeof WebSAGE.g_win_cmd.window == "object")
    if (WebSAGE.g_win_cmd.window) {
      // fecha janela info
      WebSAGE.g_win_cmd.window.close();
    }

  setTimeout(WebSAGE.showValsInfo0, 200);
},

// busca dado do ponto tempo real
showValsInfo0: function() {

var arrpnt = [WebSAGE.g_nponto_sup];
WebSAGE.getRealtimeData( arrpnt, true,
    prop => { 
    NPTO = WebSAGE.g_nponto_sup; 
    VAL_STR = S[WebSAGE.g_nponto_sup];
    ESTACAO = SUBS[WebSAGE.g_nponto_sup];
    ST_ON = STONS[WebSAGE.g_nponto_sup];
    ST_OFF = STOFS[WebSAGE.g_nponto_sup];
    UNIDADE = prop.unit;
    DESC = DCRS[WebSAGE.g_nponto_sup];
    MODULO = BAYS[WebSAGE.g_nponto_sup];
    LIMS = LIMSUPS[WebSAGE.g_nponto_sup];
    LIMI = LIMINFS[WebSAGE.g_nponto_sup];
    HISTER = prop.hysteresis;
    ALRIN = prop.alarmDisabled;
    ID = TAGS[WebSAGE.g_nponto_sup];
    setTimeout(WebSAGE.showValsInfo1, 100);
    }
  );  
},

// Abre uma janela popup com dados sobre o ponto
showValsInfo1: function() {
  
  // abre nova janela, dá um tempo e vai  preencher os dados da nova janela em outra funcao
  // (para dar tempo de abrir a janela)
  WebSAGE.g_win_1stdraw = 1;
  WebSAGE.g_win_cmd = window.open(
    "dlginfo.html",
    "wsinfo",
    "dependent=yes,height=620,width=400,toolbar=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,modal=yes"
  );
  WebSAGE.g_tminfoID = setTimeout("WebSAGE.g_win_cmd.close()", 3000);
  WebSAGE.g_wait_win = 0; // contador para esperar abrir a janela

  // showValsInfo2 será chamado pela própria nova janela aberta em onload
},

// Mostra os dados sobre o ponto em janela popup
showValsInfo2: function() {
  try {
    // test for dialog window opened
    if (
      NPTO === 0 ||
      typeof WebSAGE.g_win_cmd.window !== "object" ||
      WebSAGE.g_win_cmd.window === null ||
      typeof WebSAGE.g_win_cmd.window.closed === "undefined" ||
      WebSAGE.g_win_cmd.window.closed
    ) {
      return; // give up
    }

    // janela carregada
    var se = ESTACAO;
    se = se + "-";

    WebSAGE.writeElemByIdWnd(
      WebSAGE.g_win_cmd,
      "VALOR_SUP",
      S[NPTO]
      // roundnum(V[NPTO], 4) + " " + UNIDADE + " (" + Msg.QValor + ")"
    );
    WebSAGE.writeElemByIdWnd(
      WebSAGE.g_win_cmd,
      "ESTADO_SUP",
      S[NPTO]
    );

    var SQ = "";
    var Q = WebSAGE.getFlags(NPTO);
/*
    if ((Q & 0x03) == 0x00) {
      WebSAGE.writeElemByIdWnd(
        WebSAGE.g_win_cmd,
        "ESTADO_SUP",
        Msg.QDPIntermed + " (" + Msg.EstadoAtual + ")"
      );
    } else if ((Q & 0x03) == 0x03) {
      WebSAGE.writeElemByIdWnd(
        WebSAGE.g_win_cmd,
        "ESTADO_SUP",
        Msg.QDPInvalido + " (" + Msg.EstadoAtual + ")"
      );
    } else if (V[NPTO] & (0x01 != 0)) {
      WebSAGE.writeElemByIdWnd(
        WebSAGE.g_win_cmd,
        "ESTADO_SUP",
        ST_OFF + " (" + Msg.EstadoAtual + ")"
      );
    } // não zero é off
    else {
      WebSAGE.writeElemByIdWnd(
        WebSAGE.g_win_cmd,
        "ESTADO_SUP",
        ST_ON + " (" + Msg.EstadoAtual + ")"
      );
    } // zero é on
*/

    if (Q & 0x80) {
      SQ += Msg.QFalhado + " ";
    }
    if (Q & 0x10) {
      SQ += Msg.QSubst + " ";
    }
    if ((Q & 0x0c) == 0x04) {
      SQ += Msg.QCalculado + " ";
    } else if ((Q & 0x0c) == 0x0c) {
      SQ += Msg.QManual + " ";
    } else if ((Q & 0x0c) == 0x08) {
      SQ += Msg.QNuncaAtu + " ";
    }
    if (Q & 0x100) {
      SQ += Msg.QAlarmado + " ";
    }
    //if ( Q&0x200 )
    //  SQ += Msg.QAnotacao+' ';
    if (Q & 0x400) {
      SQ += Msg.QAlmInib + " ";
    }
    if (Q & 0x800) {
      SQ += Msg.QNaoNormal + " ";
    }
    if (Q & 0x1000) {
      SQ += Msg.QCongelado + " ";
    }
    if (SQ == "") {
      SQ = Msg.QNormal + " ";
    }

    WebSAGE.writeElemByIdWnd(
      WebSAGE.g_win_cmd,
      "QUALIF",
      Msg.Qualific + ": " + SQ
    );

    if (WebSAGE.g_win_1stdraw) {
      // escreve parâmetros só na primeira vez que abriu a janela
      clearTimeout(WebSAGE.g_tminfoID);
      WebSAGE.g_win_1stdraw = 0;
      WebSAGE.writeElemByIdWnd(WebSAGE.g_win_cmd, "NPONTO_SUP", NPTO + ":" + ID);
      WebSAGE.writeElemByIdWnd(WebSAGE.g_win_cmd, "DESCR_SUP", se + DESC);
      WebSAGE.writeElemByIdWnd(
        WebSAGE.g_win_cmd,
        "SPCMDINTERTRAV",
        Titles.SPCMDINTERTRAV
      );
      WebSAGE.g_win_cmd.document.getElementById("TABULAR").style.display = "";
      //WebSAGE.g_win_cmd.document.getElementById("TABULAR").href="tabular.html?SELMODULO="+ID.substring(0,9);
      Core.addEventListener(
        WebSAGE.g_win_cmd.document.getElementById("TABULAR"),
        "click",
        WebSAGE.tabular
      );

      if (ID.charAt(21) == "M") {
        // Manual não apresenta opção de inibir
        WebSAGE.g_win_cmd.document.getElementById("DIVINIB").style.display =
          "none";
      }

      WebSAGE.g_win_cmd.document.getElementById("CURVAS").style.display = "";
      Core.addEventListener(
        WebSAGE.g_win_cmd.document.getElementById("CURVAS"),
        "click",
        WebSAGE.curvas
      );

      if (Q & 0x20) {
        // mostra parâmetros de limites só para pontos analógicos
        WebSAGE.g_win_cmd.document.getElementById(
          "TENDENCIAS"
        ).style.display = "";
        Core.addEventListener(
          WebSAGE.g_win_cmd.document.getElementById("TENDENCIAS"),
          "click",
          WebSAGE.tendencias
        );

        WebSAGE.g_win_cmd.document.getElementById("VALOR_HID").style.display =
          "";
        WebSAGE.g_win_cmd.document.getElementById("LIMCTRLS").style.display =
          "";
        WebSAGE.g_win_cmd.document.getElementById("LIMSUP").value = LIMS;
        WebSAGE.g_win_cmd.document.getElementById("LIMINF").value = LIMI;
        WebSAGE.g_win_cmd.document.getElementById("HISTER").value = HISTER;
        Core.addEventListener(
          WebSAGE.g_win_cmd.document.getElementById("LIMSUP"),
          "blur",
          WebSAGE.writeProperties
        );
        Core.addEventListener(
          WebSAGE.g_win_cmd.document.getElementById("LIMINF"),
          "blur",
          WebSAGE.writeProperties
        );
        Core.addEventListener(
          WebSAGE.g_win_cmd.document.getElementById("HISTER"),
          "blur",
          WebSAGE.writeProperties
        );
        if (ID.charAt(21) == "M" || SIMULACAO == 1 || SIMULACAO == 2) {
          // permite alterar valor de ponto manual
          WebSAGE.g_win_cmd.document.getElementById(
            "DIVALTVALOR"
          ).style.display = "";
          Core.addEventListener(
            WebSAGE.g_win_cmd.document.getElementById("CBALTVALOR"),
            "click",
            function() {
              WebSAGE.g_win_cmd.document.getElementById(
                "CBALTVALOR"
              ).style.display = "none";
              WebSAGE.g_win_cmd.document.getElementById(
                "NOVOVALOR"
              ).style.display = "";
            }
          );
          Core.addEventListener(
            WebSAGE.g_win_cmd.document.getElementById("NOVOVALOR"),
            "blur",
            WebSAGE.writeProperties
          );
        }
      }
      WebSAGE.g_win_cmd.document.getElementById(
        "ANOTACAO"
      ).value = ANOTS[NPTO].replace(/\|\^/g, "\n");
      WebSAGE.g_win_cmd.document.getElementById("CBALRIN").checked =
        ALRIN != "0";
      Core.addEventListener(
        WebSAGE.g_win_cmd.document.getElementById("CBALRIN"),
        "click",
        WebSAGE.writeProperties
      );
      Core.addEventListener(
        WebSAGE.g_win_cmd.document.getElementById("ANOTACAO"),
        "blur",
        WebSAGE.writeProperties
      );
      Core.addEventListener(
        WebSAGE.g_win_cmd.document.getElementById("CBBLKCMD"),
        "click",
        WebSAGE.writeProperties
      );

      if (!(Q & 0x20)) {
        // ponto digital
        WebSAGE.g_win_cmd.document.getElementById(
          "ESTADO_HID"
        ).style.display = "";
        if (ID.charAt(21) == "M" || SIMULACAO == 1 || SIMULACAO == 2) {
          // permite alterar valor de ponto manual
          WebSAGE.g_win_cmd.document.getElementById(
            "DIVALTVALOR"
          ).style.display = "";

          Core.addEventListener(
            WebSAGE.g_win_cmd.document.getElementById("CBALTVALOR"),
            "click",
            function() {
              WebSAGE.g_win_cmd.document.getElementById(
                "CBALTVALOR"
              ).style.display = "none";
              WebSAGE.g_win_cmd.document.getElementById(
                "DIVALTVALORDIG"
              ).style.display = "";
            }
          );

          WebSAGE.g_win_cmd.document.getElementById(
            "rbNovoValor"
          ).nextSibling.data = ST_ON;
          WebSAGE.g_win_cmd.document.getElementById(
            "rbNovoValorOff"
          ).nextSibling.data = ST_OFF;
          Core.addEventListener(
            WebSAGE.g_win_cmd.document.getElementById("rbNovoValor"),
            "click",
            WebSAGE.writeProperties
          );
          Core.addEventListener(
            WebSAGE.g_win_cmd.document.getElementById("rbNovoValorOff"),
            "click",
            WebSAGE.writeProperties              
          );
        }
      }

      // get nonblocking annotation
      WebSAGE.g_win_cmd.document.getElementById("ANOTACAODOC").value = DNOTES[NPTO];

      Core.addEventListener(
        WebSAGE.g_win_cmd.document.getElementById("ANOTACAODOC"),
        "blur",
        WebSAGE.writeProperties
      );
    }

    if (!userHasRight('sendCommands'))
      WebSAGE.g_win_cmd.document.getElementById('COMANDAR').disabled = true;
    if (!userHasRight('enterAnnotations'))
      WebSAGE.g_win_cmd.document.getElementById('ANOTACAO').disabled = true;
    if ( (F[NPTO] & 0x0c) == 0x0c && !userHasRight('enterManuals'))
      WebSAGE.g_win_cmd.document.getElementById('CBALTVALOR').disabled = true;
    if (!userHasRight('substituteValues'))
      WebSAGE.g_win_cmd.document.getElementById('CBALTVALOR').disabled = true;
    if (!userHasRight('enterNotes'))
      WebSAGE.g_win_cmd.document.getElementById('ANOTACAODOC').disabled = true;
    if (!userHasRight('disableAlarms'))
      WebSAGE.g_win_cmd.document.getElementById('CBALRIN').disabled = true;
    if (!userHasRight('enterLimits')) {
      WebSAGE.g_win_cmd.document.getElementById('LIMSUP').disabled = true;
      WebSAGE.g_win_cmd.document.getElementById('LIMINF').disabled = true;
      WebSAGE.g_win_cmd.document.getElementById('HISTER').disabled = true;
    }
    
    var arrpnt = [WebSAGE.g_nponto_sup];
    WebSAGE.getRealtimeData( arrpnt, true,
          prop => { 
          NPTO = WebSAGE.g_nponto_sup; 
          VAL_STR = S[WebSAGE.g_nponto_sup];
          ESTACAO = SUBS[WebSAGE.g_nponto_sup];
          ST_ON = STONS[WebSAGE.g_nponto_sup];
          ST_OFF = STOFS[WebSAGE.g_nponto_sup];
          UNIDADE = prop.unit;
          DESC = DCRS[WebSAGE.g_nponto_sup];
          LIMS = LIMSUPS[WebSAGE.g_nponto_sup];
          LIMI = LIMINFS[WebSAGE.g_nponto_sup];
          HISTER = prop.hysteresis;
          ALRIN = prop.alarmDisabled;
          ID = TAGS[WebSAGE.g_nponto_sup];
          }
        );
     setTimeout(WebSAGE.showValsInfo2, 1000);                    
        
  } catch (err) {
    $("#SP_STATUS").text(err.name + ": " + err.message + " [2]");
    document.getElementById("SP_STATUS").title = err.stack;
  }
},

// open trend visor of point info
tendencias: function() {
  WebSAGE.vis_trend(NPTO);
},

// open trend visor
vis_trend: function(npt) {
  window.open(
    "trend.html?NPONTO=" + npt,
    "Tendencias " + npt,
    "dependent=no,height=400,width=700,location=no,toolbar=no,directories=no,status=no,menubar=no,resizable=yes,modal=no"
  );
  setTimeout("WebSAGE.g_win_cmd.close()", 500);
},

// open plot visor (historical) of point info
curvas: function() {
  if (F[NPTO] & 0x20)
    window.open(
      "/grafana/d/78X6BmvMk/json-scada-history-analog?var-point_tag=" + TAGS[NPTO],
      "History " + TAGS[NPTO],
      "dependent=no,height=600,width=1000,location=no,toolbar=no,directories=no,status=no,menubar=no,resizable=yes,modal=no"
    );
  else
    window.open(
      "/grafana/d/LsXOaz47z/json-scada-history-digital?var-point_tag=" + TAGS[NPTO],
      "History " + TAGS[NPTO],
      "dependent=no,height=600,width=1000,location=no,toolbar=no,directories=no,status=no,menubar=no,resizable=yes,modal=no"
    );
  setTimeout("WebSAGE.g_win_cmd.close()", 500);
},

// open tabular visor of bay, of point info
tabular : function()
{
window.open( 'tabular.html?SUBST=' + ESTACAO + '&BAY=' + MODULO, 'Tabular', 'dependent=no,height=700,width=900,location=no,toolbar=no,directories=no,status=no,menubar=no,resizable=yes,modal=no' );    
setTimeout('WebSAGE.g_win_cmd.close()',500);
},

writeProperties: function() {
    if (!WebSAGE.g_win_cmd) {
      return;
    }
    if (typeof WebSAGE.g_win_cmd.window != "object") {
      return;
    }
    if (typeof WebSAGE.g_win_cmd.window.closed === "undefined") {
      return;
    }
    if (WebSAGE.g_win_cmd.window.closed) {
      return;
    }
    if (WebSAGE.g_win_cmd.document === undefined) {
      return;
    }

    if (WebSAGE.g_win_cmd.document.getElementById("CBBLKCMD").checked) {
      // desbloqueio do comando apaga anotação
      WebSAGE.g_win_cmd.document.getElementById("ANOTACAO").value = "";
    }

    var li = parseFloat(
      WebSAGE.g_win_cmd.document.getElementById("LIMINF").value
    );
    if (isNaN(li)) {
      li = -Number.MAX_VALUE;
    }

    var ls = parseFloat(
      WebSAGE.g_win_cmd.document.getElementById("LIMSUP").value
    );
    if (isNaN(ls)) {
      ls = Number.MAX_VALUE;
    }

    var hs = parseFloat(
      WebSAGE.g_win_cmd.document.getElementById("HISTER").value
    );
    if (isNaN(hs)) {
      hs = 0;
    }

    LIMINFS[NPTO] = li;
    LIMSUPS[NPTO] = ls;

    //WebSAGE.g_win_cmd.document.getElementById("LIMINF").value = li;
    //WebSAGE.g_win_cmd.document.getElementById("LIMSUP").value = ls;
    //WebSAGE.g_win_cmd.document.getElementById("HISTER").value = hs;

    ANOTS[NPTO] = WebSAGE.g_win_cmd.document.getElementById("ANOTACAO").value;
    DNOTES[NPTO] = WebSAGE.g_win_cmd.document.getElementById("ANOTACAODOC").value;

    var valueDefault = 0;
    if (WebSAGE.getFlags(NPTO) & 0x20) {
      valueDefault = WebSAGE.g_win_cmd.document.getElementById("NOVOVALOR").value;
    } else {
      valueDefault = WebSAGE.g_win_cmd.document.getElementById("rbNovoValor").checked
        ? true
        : false;
    }

    // use OPC web hmi protocol https://prototyping.opcfoundation.org/
    var ServiceId = OpcServiceCode.WriteRequest // write data service
    var RequestHandle = Math.floor(Math.random() * 100000000)
    var req = {
      ServiceId: ServiceId,
      Body: {
        RequestHeader: {
          Timestamp: new Date().toISOString(),
          RequestHandle: RequestHandle,
          TimeoutHint: 1250,
          ReturnDiagnostics: 2,
          AuthenticationToken: null
        },
        NodesToWrite: [
          {
            NodeId: {
              IdType: OpcKeyType.Numeric, // type: numeric key
              Id: parseInt(NPTO), // numeric key for the point
              Namespace: OpcNamespaceMongodb
            },
            AttributeId: OpcAttributeId.Description, // OPC attribute to write: Description
            Value: {
              _Properties: {
                alarmDisabled: WebSAGE.g_win_cmd.document.getElementById('CBALRIN')
                  .checked
                  ? true
                  : false,
                annotation: ANOTS[NPTO],
                loLimit: li,
                hiLimit: ls,
                hysteresis: hs,
                notes: DNOTES[NPTO],
                valueDefault: valueDefault
              }
            }
          }
        ]
      }
    }

    fetchTimeout("/Invoke/", 1250, {
      method: "POST",
      body: JSON.stringify(req),
      headers: {
        "Content-Type": "application/json"
      }
    })
      .then(function (response) {
        return response;
      })
      .then(response => response.json())
      .then(data => {
        if (!data.ServiceId || !data.Body || !data.Body.ResponseHeader || !data.Body.ResponseHeader.RequestHandle || !data.Body.Results){
          console.log("Error writing point data! Invalid or unexpected response.");
          return;
        }
        if (data.ServiceId !== OpcServiceCode.WriteResponse || data.Body.ResponseHeader.ServiceResult != 0){
          console.log("Error writing point data! Service error.");
          return;
        }
        if (data.Body.ResponseHeader.RequestHandle !== RequestHandle){
          console.log("Error writing point data! Invalid response handle.");
          return;
        }

        // success
        // console.log("write ok");
      });
  },

zeraHoraInicial : function()
{ 
document.getElementById("HHORAINI").value = "00:00:00";
},

dataAtual : function()
{
document.getElementById("HDATAINI").value = dateToYMDString( new Date() );
},

apagaFiltro : function()
{
document.getElementById("HFILTRO").value = "";
},

// obtains realtime data from the server translating it to 
// internal data structures the call the SVG screen update routine
getRealtimeData: function (querykeys, askinfo, callbacksuccess) {
  if (typeof querykeys.length !== "number" || querykeys.length == 0)
      return;
  
  // use OPC web hmi protocol https://prototyping.opcfoundation.org/
  var ServiceId = OpcServiceCode.ReadRequest // read data service
  var RequestHandle = Math.floor(Math.random() * 100000000)
  var req = {
    ServiceId: ServiceId,
    Body: {
      RequestHeader: {
        Timestamp: new Date().toISOString(),
        RequestHandle: RequestHandle,
        TimeoutHint: 1500,
        ReturnDiagnostics: 2,
        AuthenticationToken: null
      },
      MaxAge: 0,
      TimestampsToReturn: TimestampsToReturn.Both
    }
  }

  var NodesToRead = [];
  querykeys.map(element => {
    var IdType, Id;
    if (isNaN(parseInt(element))) { // a tag
      IdType = OpcKeyType.String;
      Id = element;
    }
    else { // a numeric key
      IdType = OpcKeyType.Numeric;
      Id = parseInt(element);
    }
    NodesToRead.push({
      "NodeId": {
        "IdType": IdType,
        "Id": Id,
        "Namespace": OpcNamespaceMongodb
      },
      "AttributeId": (askinfo === true ? OpcAttributeId.Description : OpcAttributeId.Value)
    });
    return element;
  });
  req.Body.NodesToRead = NodesToRead;

  fetchTimeout("/Invoke/", 1500, {
    method: "POST",
    body: JSON.stringify(req),
    headers: {
      "Content-Type": "application/json"
    }
  })
    .then(function (response) {
      return response;
    })
    .then(response => response.json())
    .then(data => {
      if (!data.ServiceId || !data.Body || !data.Body.ResponseHeader || !data.Body.ResponseHeader.RequestHandle){
        console.log("ReadRequest invalid service response!");
        return;
        }

      // response must have same request handle and be a read response or service fault 
      if (data.Body.ResponseHeader.RequestHandle !== RequestHandle ||
          (data.ServiceId !== OpcServiceCode.ReadResponse && data.ServiceId !== OpcServiceCode.ServiceFault)
          ){
        console.log("ReadRequest invalid or unexpected service response!");
        return;
        }
      if ( data.Body.ResponseHeader.ServiceResult !== OpcStatusCodes.Good && 
           data.Body.ResponseHeader.ServiceResult !== OpcStatusCodes.GoodNoData ) {
          console.log("ReadRequest service error!");
          // check access control denied, in this case go to initial page
          if ( data.Body.ResponseHeader.ServiceResult === OpcStatusCodes.BadUserAccessDenied ||
               data.Body.ResponseHeader.ServiceResult === OpcStatusCodes.BadIdentityTokenInvalid ||
               data.Body.ResponseHeader.ServiceResult === OpcStatusCodes.BadIdentityTokenRejected 
           ) {
               window.onbeforeunload = null;
               window.location.href = "/";
           }
          return;
        }

      WebSAGE.Pass++;

      var prop;
      data.Body.Results.map(element => {
        if (typeof element.StatusCode == "number" && element.StatusCode != 0) // reject a bad result
          return;
        if (element.NodeId.IdType != 1)
          return;
        prop = element._Properties;          
        var pointKey = prop._id;

        if (element.Value.Type === OpcValueTypes.Boolean) {
            V[pointKey] = element.Value.Body?0:1;
            F[pointKey] = (element.Value.Body ? 0x02 : 0x01);
            T[pointKey] = element.SourceTimestamp;
            S[pointKey] = prop.valueString;
          }
        else if (element.Value.Type === OpcValueTypes.Double) {
            V[pointKey] = element.Value.Body;
            F[pointKey] = 0x20;
            T[pointKey] = element.SourceTimestamp;
            S[pointKey] = prop.valueString;
          }
        else if (element.Value.Type === OpcValueTypes.String) { 
            V[pointKey] = parseFloat(element.Value.Body);
            F[pointKey] = 0x00;
            T[pointKey] = element.SourceTimestamp;
            S[pointKey] = element.Value.Body;
          }
        F[pointKey] |= (element.Value.Quality & 0x80000000 ? 0x80 : 0x00) | 
                        (prop.alarmed ? 0x100 : 0x000) |
                        (prop.alarmDisabled ? 0x400 : 0x000) |
                        (("annotation" in prop && prop.annotation!=="")? 0x200 : 0x000);

        TAGS[pointKey] = element.NodeId.Id;
        NPTS[element.NodeId.Id] = pointKey;
        if (typeof prop.description == "string") {
          SUBS[pointKey] = prop.group1;
          BAYS[pointKey] = prop.group2;
          DCRS[pointKey] = prop.description;
          if (isNaN(prop.hiLimit) || prop.hiLimit === null)
            LIMSUPS[pointKey] = Infinity;
          else  
            LIMSUPS[pointKey] = prop.hiLimit;
          if (isNaN(prop.loLimit) || prop.loLimit === null)
            LIMINFS[pointKey] = -Infinity;
          else
            LIMINFS[pointKey] = prop.loLimit;
          STONS[pointKey] = prop.stateTextTrue;
          STOFS[pointKey] = prop.stateTextFalse;
          ANOTS[pointKey] = prop.annotation;
          DNOTES[pointKey] = prop.notes;
        }
        return element;
      });
      if ( typeof callbacksuccess == "function" )
          callbacksuccess(prop);
    })
    .catch(function (error) {
      console.log(error);
    });
},

histBusca : function()
{
WebSAGE.g_tbl.clearAll();

var group1Filter = [];
if (document.getElementById("HFILTRO").value.trim() !== "")
  group1Filter.push(document.getElementById("HFILTRO").value);

var timePieces = document.getElementById("HHORAINI").value.split(':');    
if (timePieces.length == 2)
  timePieces[2] = 0;

var timeBegin = new Date(document.getElementById("HDATAINI").value);
timeBegin.setTime(timeBegin.getTime() + 
                  ((timeBegin.getTimezoneOffset()/60)*60*60*1000) + 
                  (timePieces[0]*60*60*1000) + (timePieces[1]*60*1000) + (timePieces[2]*1000));

var timeEnd = new Date(document.getElementById("HDATAINI").value);
timeEnd.setTime(timeEnd.getTime() + 
                ((timeEnd.getTimezoneOffset()/60)*60*60*1000) + 
                (23*60*60*1000) + (59*60*1000) + (59*1000)  + 999 );

WebSAGE.getSoeData(group1Filter, WebSAGE.g_gpstime, false, WebSAGE.g_nummaxevehist, timeBegin, timeEnd);
},

getSoeData: function (group1Filter, useSourceTime, aggregate, limit, timeBegin, timeEnd) {

 if (!limit)
   limit = WebSAGE.g_nummaxevetr;

 if (timeBegin === undefined)
   timeBegin = null;
 else
   if (typeof timeBegin.getMonth === 'function')
     timeBegin = timeBegin.toISOString();

 if (timeEnd === undefined)
   timeEnd = null;
 else
   if (typeof timeEnd.getMonth === 'function')
     timeEnd = timeEnd.toISOString();

 var ContentFilter = []
 var ContFiltElem = {
        FilterOperator: OpcFilterOperator.InList,
        FilterOperands: []
        }

 for (var i=0; i<group1Filter.length; i++){
   ContFiltElem.FilterOperands.push({
    FilterOperand: OpcOperand.Literal, Value: group1Filter[i]
   });
 }

 if (ContFiltElem.FilterOperands.length > 0)
   ContentFilter.push(ContFiltElem)

 if (aggregate === 2) { // panic, filter priority also
   ContFiltElem = {
        FilterOperator: OpcFilterOperator.LessThanOrEqual,
        FilterOperands: [EventsViewer_PanicModePriorityLimit]
        }
 ContentFilter.push(ContFiltElem)
 }

 var AggregateFilter = null;   
 if (aggregate){
  AggregateFilter = { AggregateType: "Count" };
 }

 // use OPC web hmi protocol https://prototyping.opcfoundation.org/
 var ServiceId = OpcServiceCode.HistoryReadRequest // history read service
 var RequestHandle = Math.floor(Math.random() * 100000000)
 var req = {
   ServiceId: ServiceId,
   Body: {
     RequestHeader: {
       Timestamp: new Date().toISOString(),
       RequestHandle: RequestHandle,
       TimeoutHint: 5000,
       ReturnDiagnostics: 2,
       AuthenticationToken: null
     },
     TimestampsToReturn: useSourceTime? TimestampsToReturn.Source : TimestampsToReturn.Both,
     HistoryReadDetails: {
       ParameterTypeId: OpcServiceCode.ReadRawModifiedDetails,
       ParameterData: {
         IsModified: false,
         StartTime: timeBegin,
         EndTime: timeEnd,
         NumValuesPerNode: limit
       }
     },
     Namespace: OpcNamespaceMongodb, // directs query to mongodb instead of postgresql
     ContentFilter: ContentFilter,
     AggregateFilter: AggregateFilter
   }
 }

 fetchTimeout("/Invoke/", 5000, {
   method: "POST",
   body: JSON.stringify(req),
   headers: {
     "Content-Type": "application/json"
   }
 })
   .then(function (response) {
     return response;
   })
   .then(response => response.json())
   .then(data => {
    L = [];

    if (!data.ServiceId || !data.Body || !data.Body.ResponseHeader || !data.Body.ResponseHeader.RequestHandle){
        console.log("Historian invalid service response!");
        setTimeout( WebSAGE.showVals, 100 ); WebSAGE.g_waitingServer = 0;
        return;
      }
      // response must have same request handle and be a read response or service fault 
      if (data.Body.ResponseHeader.RequestHandle !== RequestHandle ||
          (data.ServiceId !== OpcServiceCode.HistoryReadResponse && data.ServiceId !== OpcServiceCode.ServiceFault)
          ){
        console.log("Historian invalid or unexpected service response!");
        setTimeout( WebSAGE.showVals, 100 ); WebSAGE.g_waitingServer = 0;
        return;
        }
      if ( data.Body.ResponseHeader.ServiceResult !== OpcStatusCodes.Good &&
            data.Body.ResponseHeader.ServiceResult !== OpcStatusCodes.GoodNoData ){
        console.log("Historian service error!");
        setTimeout( WebSAGE.showVals, 100 ); WebSAGE.g_waitingServer = 0;
        return;
      }

      if ( typeof data.Body.Results !== "object" || data.Body.Results.length === 0){
        setTimeout( WebSAGE.showVals, 100 ); WebSAGE.g_waitingServer = 0;
        return; // no data
      }

      var dateBeg;
      var date;
      data.Body.Results.map(node =>{

        var descr = node._Properties.description;
        var state = node.HistoryData[0].Value.Body;
        date = new Date(useSourceTime?node.HistoryData[0].SourceTimestamp:node.HistoryData[0].ServerTimestamp);
        if ( L.length === 0 ){
          dateBeg = date;
        }

        var countstr = "";
        if (typeof node.HistoryData[0].Value.Count === "number")
        if (node.HistoryData[0].Value.Count > 1)
          countstr = "+" + (node.HistoryData[0].Value.Count-1);

        var qualif = "" + node._Properties.priority;
        if (node.HistoryData[0].Acknowledge === 0 || node.HistoryData[0].Acknowledge === false)
          qualif = qualif + "L";
        if (node.HistoryData[0].Value.Quality !== OpcStatusCodes.Good)
          qualif = qualif + "F";
        if (useSourceTime && node.HistoryData[0].SourceTimestampOk !== true)
          qualif = qualif + "T";

        if (descr.indexOf(node._Properties.group1 + '~')===0) // remove group1 from description
          descr = descr.substring(descr.indexOf('~') + 1 );

        var dtFmt = "", tmFmt = "";
        try{
          dtFmt = date.toLocaleDateString(EventsViewer_LocaleDate, EventsViewer_LocaleDateOptions);
          tmFmt = date.toLocaleTimeString(EventsViewer_LocaleTime, EventsViewer_LocaleTimeOptions);
        }
        catch(e){
          dtFmt = date.toLocaleDateString();
          tmFmt = date.toLocaleTimeString();
        }

        L.push(
          dtFmt + WebSAGE.g_split_line_code +
          tmFmt + WebSAGE.g_split_line_code +
          ("00" + date.getUTCMilliseconds()).slice (-3) + WebSAGE.g_split_line_code +
          node._Properties.pointKey + WebSAGE.g_split_line_code +
          node.NodeId.Id + WebSAGE.g_split_line_code +
          node._Properties.group1 + WebSAGE.g_split_line_code +
          descr + WebSAGE.g_split_line_code +
          state + WebSAGE.g_split_line_code +
          0 + WebSAGE.g_split_line_code + // flags
          "" + qualif + countstr + WebSAGE.g_split_line_code + // qualif / num events
          (Math.abs(dateBeg - date) / 1000) + WebSAGE.g_split_line_code +
          node._Properties.event_id
        );
      return node;
     });
     document.getElementById("HHORAINI").value = date.toLocaleTimeString();
     setTimeout( WebSAGE.showVals, 100 ); WebSAGE.g_waitingServer = 0;
  })
  .catch(function (error) {
    L = [];
    console.log(error);
    setTimeout( WebSAGE.showVals, 100 ); WebSAGE.g_waitingServer = 0;
  });
 },

callServer : function() 
  {
      clearTimeout( WebSAGE.g_toutID );
      clearTimeout( WebSAGE.g_ServerStatusID );

      if ( WebSAGE.g_waitingServer ) // se ainda está esperando pelo servidor
         { // dá mais um tempo para rechamar-se e cai fora
         WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, WebSAGE.g_timeOutRefresh / 2 );
         return;
         }       

      if ( document.getElementById("idHistorico").checked )
        return;

      var agregar = 0;
      
      if ( document.getElementById("idNormal").checked )
        agregar = 0;

      if ( document.getElementById("idAgregar").checked )
        agregar = 1;

      if ( document.getElementById("idPanico").checked )
        agregar = 2;
      
      // testa se não está bloqueado
      if ( document.getElementById("idCongelar").checked == false )
        {
        Ventoinha.pulse();

        // prepara uma função para o caso de dar timeout na chamada do servidor
	    	clearTimeout( WebSAGE.g_timeOutFalha );
        WebSAGE.g_timeoutFalhaID = setTimeout( WebSAGE.falhaTudo, WebSAGE.g_timeOutFalha );

        cbf_F = function() { setTimeout( WebSAGE.showVals, 100 ); WebSAGE.g_waitingServer = 0; };
        WebSAGE.g_waitingServer = 1;
        
        // verifica quais os filtros marcados pelo operador, se todos estão marcados passa filtro vazio
        var filtro = [];
        WebSAGE.g_filtro_se = "";
        var cntflt = 0;
        if ( EventsViewer_AllowFilter && typeof formfiltro !== "undefined" )
          {  
          var i;
          var todos = true;        
          for ( i = 0; i < formfiltro.length ; i++ )
             {
            if ( formfiltro[i].checked )
              {
              cntflt++;
              filtro.push(formfiltro[i].value);
              if ( cntflt < 4 )
                WebSAGE.g_filtro_se = WebSAGE.g_filtro_se + formfiltro[i].value + " ";
              else   
              if ( cntflt == 4 )
                WebSAGE.g_filtro_se = WebSAGE.g_filtro_se + "... ";
              }
            else
              todos = false;
           }
          if ( todos )  
            filtro = [];
          if ( filtro.length === 0 )
            setOpacity( document.getElementById( "imgFilter" ), 40 );
          else
            setOpacity( document.getElementById( "imgFilter" ), 100 );
          }

        var begDate = new Date();
        begDate.setDate(begDate.getDate()-30);
        WebSAGE.getSoeData(filtro, WebSAGE.g_gpstime, agregar, WebSAGE.g_nummaxevetr);

        WebSAGE.g_pass++;
        
        // vou testar o status do webserver para ver se houve alguma mudança 
        WebSAGE.g_ServerStatusID = setTimeout( WebSAGE.getServerStatus, WebSAGE.g_timeOutServerStatus );
        // próximo refresh
        WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, WebSAGE.g_timeOutRefresh );
        }

},

// pega status do servidor, variável ALARMBEEP
getServerStatus: function()
{
  WebSAGE.getRealtimeData( [BEEP_POINTKEY, CNTUPDATES_POINTKEY], true,
      prop => {
        ALARMBEEP = WebSAGE.getValue(BEEP_POINTKEY);
 
        if ( ALARMBEEP ) {
          if ( ! document.getElementById( "idHistorico" ).checked )    
          if ( document.getElementById( "imgAlarmes" ).style.display != "" ) {
              document.getElementById( "imgAlarmes" ).style.display = "";
              document.getElementById( "IMGEVENTOS" ).style.display = "none";
              document.getElementById( "SILENCIA_ID" ).style.display = "";
            }
          }  
        else {
          if ( document.getElementById( "imgAlarmes" ).style.display != "none" ) {
              document.getElementById( "imgAlarmes" ).style.display = "none";
              document.getElementById( "SILENCIA_ID" ).style.display = "none";	      
              document.getElementById( "IMGEVENTOS" ).style.display = "";
            }
          }

      // se mudou estado de alarme, vou atualizar lista logo
      if ( NUM_VAR_ANT != WebSAGE.getValue(CNTUPDATES_POINTKEY) ) {
        clearTimeout( WebSAGE.g_toutID );
        // refresh events after some time (~2s) enough to let the event(s) be recorded
        WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, WebSAGE.g_timeOutServerStatus );
      }
      else {
        // prepara novo teste de status
        WebSAGE.g_ServerStatusID = setTimeout( WebSAGE.getServerStatus, WebSAGE.g_timeOutServerStatus );
        }

      NUM_VAR_ANT = WebSAGE.getValue(CNTUPDATES_POINTKEY);
      });  
},

// Mostra os eventos recebidos
showVals: function()
{
var dbg = 0;

clearTimeout( WebSAGE.g_timeoutFalhaID ); 

try
  {
  //console.profile();
  
    // só mostra se vier uma lista diferente da que já havia
    var tam = L.length;
    var nr;
    
    dbg = 1;
    for( var i in L )
      {
      if ( i >= WebSAGE.g_tbl.getRowsNum() )
         {
          dbg = 1.6; // bug intermitente do chrome

          WebSAGE.g_tbl.addRow( i, L[i] );
          if ( WebSAGE.g_subst_list.indexOf( WebSAGE.g_tbl.cellsx( i, WebSAGE.g_COL_SUBEST ) ) == -1 )
            {
            WebSAGE.g_subst_list.push( WebSAGE.g_tbl.cellsx( i, WebSAGE.g_COL_SUBEST ) );
            }
          WebSAGE.EstiloLinha( i, 0 );
         }
      else
        {       
        dbg = 1.1; // bug intermitente do chrome

        if ( L[i] == WebSAGE.g_tbl.getUserData(i) ) // enquanto for igual, mantém
            continue;
  
        WebSAGE.g_tbl.changeRow( i, L[i] );
        if ( WebSAGE.g_subst_list.indexOf( WebSAGE.g_tbl.cellsx( i, WebSAGE.g_COL_SUBEST ) ) == -1 )
          {
          WebSAGE.g_subst_list.push( WebSAGE.g_tbl.cellsx( i, WebSAGE.g_COL_SUBEST ) );
          }
        WebSAGE.EstiloLinha( i, 0 );
        }
      }

  while ( tam < WebSAGE.g_tbl.getRowsNum() ) // para caso onde ficam entrando e saindo linhas (qdo mostra tudo anormal)) 
     {
        WebSAGE.g_tbl.delRow( WebSAGE.g_tbl.getRowsNum() - 1 );
     }

  // se tem eventos não mostrados, no histórico, coloca a hora inicial do último evento para nova consulta
  if ( document.getElementById("idHistorico").checked )
  if ( WebSAGE.g_tbl.getRowsNum() >= WebSAGE.g_nummaxevehist )
  { 
  nr = WebSAGE.g_tbl.getRowsNum() - 1;
  var vc = WebSAGE.g_tbl.cellsx( nr , WebSAGE.g_COL_ID );
  var mxe = vc.indexOf("MAX_EVT");
  if ( mxe != -1 )
    {
    document.getElementById("HHORAINI").value = WebSAGE.g_tbl.cellsx( WebSAGE.g_tbl.getRowsNum()-2, WebSAGE.g_COL_HORA );
    }
  }

  var txt = WebSAGE.g_tbl.getRowsNum() + " " + Msg.Eventos;
  if ( WebSAGE.g_filtro_se != "" && WebSAGE.g_modo !== 4 )
    txt = txt + " " + Msg.EveFiltradosSE + " ( " + WebSAGE.g_filtro_se + " )"; 
  
  if ( document.getElementById("HFILTRO").value.trim() != "" && WebSAGE.g_modo === 4 )
    txt = txt + " " + Msg.EveFiltradosSE + " ( " + document.getElementById("HFILTRO").value.trim() + " )"; 
    
  var obj = document.getElementById("NUMREGS");
  if ( obj.innerHTML )
     obj.innerHTML = txt;
  else    
     obj.nextSibling.data = txt;

  // console.profileEnd();
    
  // Process notifications if enabled and not in historical mode
  if ( EventsViewer_Notific )
  if ( ! document.getElementById("idHistorico").checked )
    {
    setTimeout( WebSAGE.processaNotific, WebSAGE.g_timeOutServerStatus / 2 );
    }

  Ventoinha.pulse(".");
  }
catch (e)
  { 
  Ventoinha.pulse("E");
  }    
},

// acknowledges or quits event alarm
ackAlarm: function( almquit, aggregate, pointId, evtid )
{
if (almquit === undefined)
  almquit = 0;
if (aggregate === undefined)
  aggregate = 0;
if (pointId === undefined)
  pointId = 0;
if (evtid === undefined)
  evtid = 0;

  var action = 0;
  if (almquit === 1 && pointId === 0){ // remove all events
     action = OpcAcknowledge.RemoveAllEvents | OpcAcknowledge.SilenceBeep;
  }
  if (almquit === 0 && pointId === 0){ // ack all events
     action = OpcAcknowledge.AckAllEvents | OpcAcknowledge.SilenceBeep;
  }
  if (almquit === -1){ // only silcence beep
     action = OpcAcknowledge.SilenceBeep;
  }
  if (almquit === 0 && aggregate === 1 && pointId !== 0){ // ack all events for a point
     action = OpcAcknowledge.AckPointEvents | OpcAcknowledge.SilenceBeep;
  }
  if (almquit === 1 && aggregate === 1 && pointId !== 0){ // remove all events for a point
     action = OpcAcknowledge.RemovePointEvents | OpcAcknowledge.SilenceBeep;
  }
  if (almquit === 0 && aggregate === 0 && pointId !== 0){ // ack one event
     action = OpcAcknowledge.AckOneEvent | OpcAcknowledge.SilenceBeep;
  }
  if (almquit === 1 && aggregate === 0 && pointId !== 0){ // remove one event
     action = OpcAcknowledge.RemoveOneEvent | OpcAcknowledge.SilenceBeep;
  }
  
    var IdType = OpcKeyType.Numeric;
    if ( isNaN(parseInt(pointId)) )
      IdType = OpcKeyType.String
    else  
      pointId = parseInt(pointId);

    // use OPC web hmi protocol https://prototyping.opcfoundation.org/
    var ServiceId = OpcServiceCode.WriteRequest // write data service
    var RequestHandle = Math.floor(Math.random() * 100000000)
    var req = {
      ServiceId: ServiceId,
      Body: {
        RequestHeader: {
          Timestamp: new Date().toISOString(),
          RequestHandle: RequestHandle,
          TimeoutHint: 1000,
          ReturnDiagnostics: 2,
          AuthenticationToken: null
        },
        NodesToWrite: [
          {
            NodeId: {
              IdType: IdType, // type: numeric or string key
              Id: pointId, // key for the point
              Namespace: OpcNamespaceMongodb
            },
            AttributeId: OpcAttributeId.ExtendedAlarmEventsAck, // OPC attribute to write: Alarm Ack
            Value: {
              Type: OpcValueTypes.Integer,
              Body: action
            },
            _Properties: {
              event_id: evtid
            }
          }
        ]
      }
    }

    fetchTimeout("/Invoke/", 1500, {
      method: "POST",
      body: JSON.stringify(req),
      headers: {
        "Content-Type": "application/json"
      }
    })
      .then(function (response) {
        return response;
      })
      .then(response => response.json())
      .then(data => {
        if ( (!data.ServiceId || !data.Body || !data.Body.ResponseHeader || !data.Body.ResponseHeader.RequestHandle || !data.Body.Results) ||
             (data.ServiceId !== OpcServiceCode.WriteResponse || data.Body.ResponseHeader.RequestHandle !== RequestHandle) ||
             (data.Body.ResponseHeader.ServiceResult !== OpcStatusCodes.Good && data.Body.ResponseHeader.ServiceResult !== OpcStatusCodes.GoodNoData)) {
              console.log("Ack Event Error!");
              return;
        }

        // success
      })
      .catch(err => {
        console.log(err);
      });

},

// Desktop notifications
// Create a notificatiion when a bay circuit breaker opens (with this time) and this event in not acked
// aggregate protecions on this bay until circuit breaker closes
processaNotific: function()
{
var i, rw, semod, aux, acr, info, estdj;
var cria = {};

  if ( ! window.Notification )
    return;
  
  for ( i = L.length - 1; i >= 0 ; i-- )  
    {
    rw = L[i].split(WebSAGE.g_split_line_code);

    semod = rw[ WebSAGE.g_COL_SUBEST ] + " - " + (rw[ WebSAGE.g_COL_DESCR ].split('~'))[0]; 
  
    if (  rw[WebSAGE.g_COL_ID].indexOf( Viewers_NotificTagText ) !== -1  &&
          rw[WebSAGE.g_COL_DESCR].indexOf( Viewers_NotificDescrText ) !== -1 &&          
          rw[WebSAGE.g_COL_QUALIF].indexOf( "K" ) === -1 )
      estdj = 1;
    else
      estdj = 0;

    // se encontrou um disj ligou, resseta notificação
    if ( estdj && rw[WebSAGE.g_COL_EVENTO].indexOf( Viewers_NotificCancelEventText ) !== -1 )
       {
       delete WebSAGE.g_infomod[semod];
       delete WebSAGE.g_infomodtm[semod];
       delete WebSAGE.g_infomodnpt[semod];
       delete WebSAGE.g_infomodid[semod];
       delete WebSAGE.g_infomodsubest[semod];
       delete cria[semod];
       }

    // se não encontrou qualificado alarmado L, vai para o próximo
    if ( rw[WebSAGE.g_COL_QUALIF].indexOf( "L" ) === -1 )
       continue;
   
    if ( ( estdj && ( rw[WebSAGE.g_COL_EVENTO].indexOf( Viewers_NotificEventText ) !== -1 )) ||
           rw[WebSAGE.g_COL_EVENTO] == Viewers_AddToNotificEventText )  
      {
        if ( typeof( WebSAGE.g_infomod[ semod ] ) === "undefined" ) 
          {
          aux = "";
          }
        else
          {
          aux = WebSAGE.g_infomod[ semod ];
          }

        acr = rw[WebSAGE.g_COL_DESCR].substring( rw[WebSAGE.g_COL_DESCR].indexOf('~') + 1 );
        acr = acr + " : " + rw[WebSAGE.g_COL_EVENTO];

        if ( aux.indexOf(acr) == -1 )
          {
          info = aux + ( aux == "" ? "" : "\n") + acr;
          }
        else
          {
          info = aux;
          }

        if ( info !== WebSAGE.g_infomod[semod] )  
          { //  vai atualizar ou criar nova notificação
          cria[semod] = [ semod, info,  semod ];
          WebSAGE.g_infomod[ semod ] = info;
          if ( estdj )
            { // a estampa de tempo será a da abertura do DJ
            WebSAGE.g_infomodtm[ semod ] = /* rw[ WebSAGE.g_COL_DATA ] + " " + */ rw[ WebSAGE.g_COL_HORA ]; 
            WebSAGE.g_infomodnpt[ semod ] = rw[ WebSAGE.g_COL_NPONTO ];
            WebSAGE.g_infomodid[ semod ] = rw[ WebSAGE.g_COL_ID ];
            WebSAGE.g_infomodsubest[ semod ] = rw[ WebSAGE.g_COL_SUBEST ];
            }
          }
       }
    }

for ( var c in cria )
  {
  // se tem ao menos o estado do disjuntor e uma operação de proteção, mostra notificação
  if (  // cria[c][1].indexOf( Viewers_IDTxtHighlight2 ) != -1 &&
       cria[c][1].indexOf( Viewers_AlmTxtHighlight1 ) != -1 &&
       cria[c][1].indexOf( Viewers_DescrTxtHighlight2 ) != -1 )
    {   
    var nf = new Notification( cria[c][0] + " - " + WebSAGE.g_infomodtm[ cria[c][0] ], 
                               {
                               body: cria[c][1],
                               tag: cria[c][0] + "-" + WebSAGE.g_infomodtm[ cria[c][0] ],
                               requireInteraction: true,
                               icon: Imgs.imgNotific,
                               vibrate: [200, 100, 200],
                               renotify: false
                               } ); 

    // quando clicada a notificação, fecha processa o click, fecha também por timeout
    // nf.onclick = function() { nf.close(); WebSAGE.ackAlarm( 0, 1, WebSAGE.g_infomodnpt[ cria[c][0] ] ); };
    nf.onclick = function() { 
        nf.close(); 
        if (EventsViewer_NotificationClick) 
          EventsViewer_NotificationClick(
            WebSAGE.g_infomodnpt[ cria[c][0] ],
            WebSAGE.g_infomodid[ cria[c][0] ],
            WebSAGE.g_infomodsubest[ cria[c][0] ]
            ); 
        };
    
    setTimeout( function() { nf.close(); }, 10 * 60 * 1000 ); // timeout de 10 minutos para a notificação
    }
  }  
},

// falha todos os dados caso servidor pare de atualizar por um tempo  
falhaTudo: function()
{
clearTimeout( WebSAGE.g_ServerStatusID );
clearTimeout( WebSAGE.g_toutID );
WebSAGE.g_tbl.clearAll();
WebSAGE.g_tbl.addRow( 0, ",,,,,," + Msg.FalhaWebserver + ",,,,,", 0);
L = []; 
WebSAGE.g_waitingServer = 0;

Ventoinha.pulse( "F" );

// vai tentar de novo
clearTimeout( WebSAGE.g_toutID );
WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, WebSAGE.g_timeOutRefresh );
},

// linha selecionada: reconhece  
// evt === 0 : reconhece o evento mais recente da lista
doOnRowSelected: function( evt )
{
    var rec = false;
    var row = 0;
    
    if ( evt === 0 )
      {
          row = 0;
          rec = true;
      }
    else  
      { 
          row = evt.currentTarget.rowIndex - 1 ;
          if ( evt.altKey || evt.shiftKey )
            rec = false;
          else
            rec = true;
      }

    if ( evt.which == 2 )
      rec = true;

    if ( rec )
    {
        // ACKNOWLEDGE EVENT

        var agregar = 0;
        if ( document.getElementById("idAgregar").checked == true || WebSAGE.g_gpstime == 0 )
            agregar = 1;

        var Qualif = WebSAGE.g_tbl.cellsx( row, WebSAGE.g_COL_QUALIF );
        var PosL = Qualif.indexOf("L");

        // se já está reconhecido, vai eliminar
        if ( PosL == -1 )
        {
        WebSAGE.ackAlarm( 1, 
                          agregar, 
                          WebSAGE.g_tbl.cellsx( row, WebSAGE.g_COL_ID ),
                          WebSAGE.g_tbl.cellsx( row, WebSAGE.g_COL_EVENTID )
                        );
        // acerta a cor da linha reconhecida
        WebSAGE.EstiloLinha( row, 2 );
        }
        else
        { // não está reconhecida, vou reconhecer
        WebSAGE.ackAlarm( 0, 
                          agregar, 
                          WebSAGE.g_tbl.cellsx( row, WebSAGE.g_COL_ID ),
                          WebSAGE.g_tbl.cellsx( row, WebSAGE.g_COL_EVENTID )
                        );
        // tira o L do qualificador para permitir excluir logo em seguida do reconhecimento
        WebSAGE.g_tbl.cellsSetValue( row, WebSAGE.g_COL_QUALIF, Qualif.replace("L","") );

        // acerta a cor da linha
        WebSAGE.EstiloLinha( row, 1 );
        }
    }
    else
    { // somente silencia o alarme e abre acesso ao ponto
        WebSAGE.doSilenceBeep();
        WebSAGE.janelaInfo( WebSAGE.g_tbl.cellsx( row, WebSAGE.g_COL_NPONTO ) );
    }
},

EstiloLinha: function(id, rec)
{
try 
  {
  var stl = '';
  id = parseInt(id);
  var pri = parseInt( WebSAGE.g_tbl.cellsx( id, WebSAGE.g_COL_QUALIF ).charAt(0) );      

  if ( rec == 2 )
    { // eliminated
    stl='color: ' + EventsViewer_ElimTxtColor + ';';
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_DESCR].style.color = 'inherit';
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_EVENTO].style.color = 'inherit';
    }
  else      
  if ( WebSAGE.g_tbl.cellsx( id, WebSAGE.g_COL_QUALIF ).indexOf("L") == -1 || rec )
    { // acked
    stl = 'color: ' + EventsViewer_AckTxtColor + ';';
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.backgroundColor = ColorOfPriority[ pri ];
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.borderRadius = "10px";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.color = "black";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.textAlign = "center";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.opacity = ".4";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_DESCR].style.color = 'inherit';
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_EVENTO].style.color = 'inherit';
    }
  else    
    { // not acked (alarmed)
    if ( pri === 0 )
      {
      stl = '';
      WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_DESCR].style.color = ColorOfPriority[ pri ];
      WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_EVENTO].style.color = ColorOfPriority[ pri ];
      }
    else  
      {
      WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_DESCR].style.color = 'inherit';
      WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_EVENTO].style.color = 'inherit';
      stl = 'color: ' + EventsViewer_AlmTxtColor + ';';
      }                

    // let the qualifiers columns be in the color of priority
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.backgroundColor = ColorOfPriority[ pri ];
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.borderRadius = "10px";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.color = "black";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.textAlign = "center";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.opacity = "1";
    }

  if ( WebSAGE.g_tbl.cellsx( id, WebSAGE.g_COL_QUALIF ).indexOf("F") != -1 )
    { // falhado
    stl = 'color: ' + EventsViewer_FailTxtColor + ';';
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_DESCR].style.color = 'inherit';
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_EVENTO].style.color = 'inherit';
    }
    
  if ( pri === 0 )
    {
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_DESCR].style.fontWeight = "bold";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_EVENTO].style.fontWeight = "bold";
    }
  else 
    {
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_DESCR].style.fontWeight = "inherit";
    WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_EVENTO].style.fontWeight = "inherit";
    }			

  // represent time dif between events by a prortional top border, 1px per minute up to 1h
  var timeant = 0;
  try {
  timeant = WebSAGE.g_tbl.cellsx( id-1, WebSAGE.g_COL_TIMEDIF );
  } catch(e) {};
  var px = (WebSAGE.g_tbl.cellsx( id, WebSAGE.g_COL_TIMEDIF ) - timeant ) / 60;
  if ( px > 30 ) px = 30;
  if ( px < 1 )  px = 0;
  stl += "border-top:" + px + "px solid " + EventsViewer_GridColor + ";margin-top:" + px + "px;";
  WebSAGE.g_tbl.setRowTextStyle( id, stl );
  // WebSAGE.g_tbl.rows[id+1].style.float = "left";
  WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_DATA].style.fontSize = "smaller";
  WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_MSEC].style.fontSize = "smaller";
  WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_QUALIF].style.fontSize = "smaller";
  
  // differentiate the substation name
  WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_SUBEST].style.paddingLeft = 
    ( WebSAGE.g_subst_list.indexOf( WebSAGE.g_tbl.cellsx( id, WebSAGE.g_COL_SUBEST ) ) * 5 ) % 30 + "px"
  WebSAGE.g_tbl.rows[id+1].cells[WebSAGE.g_COL_SUBEST].style.color = 
    ColorOfSubstation[ WebSAGE.g_subst_list.indexOf( WebSAGE.g_tbl.cellsx( id, WebSAGE.g_COL_SUBEST ) ) % ColorOfSubstation.length ];
  }
catch ( e )  
  { 
  Ventoinha.pulse("E");
  }
},
      
doSilenceBeep : function()
{
  WebSAGE.ackAlarm(-1);
},      
      
doReconheceTudo: function( rec_apaga )
  {
  var row;
  
  // se tem algum não reconhecido, torna reconhecido
  for ( row = 0; row < WebSAGE.g_tbl.getRowsNum(); row++ )
    {
    if ( rec_apaga == 0 )
      WebSAGE.EstiloLinha( row, 1 );
    else  
      WebSAGE.EstiloLinha( row, 2 );
    }

    if ( rec_apaga == 0 )
      {
      WebSAGE.ackAlarm( 0, 0, 0 );
      }
	else
      {	
      WebSAGE.ackAlarm( 1, 0, 0 );
	  }			

    clearTimeout( WebSAGE.g_toutID );
	WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, WebSAGE.g_timeOutReconhece );
  },
  
ModoClick : function()
{
var titulmod = "";
var postitul = "";

window.onbeforeunload = function() { return Msg.ConfirmaSaida; };
 
if ( !document.getElementById("idCongelar").checked )
  WebSAGE.g_tbl.clearAll();

if ( WebSAGE.g_modo == 4 && !document.getElementById("idHistorico").checked )
  { // sai do modo histórico
  if ( EventsViewer_AllowFilter )
     $('#imgFilter').css('display', "" );
  document.getElementById("HISTCTRLS").style.display = "none";
  document.getElementById("imgReconheceTudo").style.display = "";
  document.getElementById("imgEliminaTudo").style.display = "";
  document.getElementById("gridbox").style.height = "calc(100% - 43px - 18px)";
  }

if ( document.getElementById("idNormal").checked )
  {
  WebSAGE.g_modo = 0;
  clearTimeout( WebSAGE.g_toutID );
  WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, 100 );
  postitul = " - "+Msg.ModoNormal;  
  }

if ( document.getElementById("idAgregar").checked )
  {
  WebSAGE.g_modo = 1;
  clearTimeout( WebSAGE.g_toutID );
  WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, 100 );
  postitul = " - " + Msg.ModoAgregado;  
  }

if ( document.getElementById("idPanico").checked )
  {
  WebSAGE.g_modo = 2;
  clearTimeout( WebSAGE.g_toutID );
  WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, 100 );
  postitul = " - " + Msg.ModoPanico;  
  }

if ( document.getElementById("idCongelar").checked )
  {
  WebSAGE.g_modo = 3;
  postitul = " - " + Msg.ModoCongelado;  
  }

if ( document.getElementById("idHistorico").checked ) 
  { // modo histórico
  // acerta posicionamento
  document.getElementById("TOOLBAR_ID").style.height = '80px';
  document.getElementById("divhead").style.top = '80px'
  document.getElementById("gridbox").style.top = '99px';
  
  clearTimeout( WebSAGE.g_toutID ); // pára de atualizar
  clearTimeout( WebSAGE.g_ServerStatusID );
  document.getElementById("HISTCTRLS").style.display = "";
  document.getElementById("imgReconheceTudo").style.display = "none";
  document.getElementById("imgEliminaTudo").style.display = "none";
  document.getElementById("imgAlarmes").style.display = "none";
  document.getElementById("IMGEVENTOS").style.display = "";
  document.getElementById("imgFilter").style.display = "none";
  document.getElementById("DIVFILT").style.display = "none";
  document.getElementById("gridbox").style.height = "calc(100% - 43px - 36px - 18px)";
  WebSAGE.zeraHoraInicial();
  WebSAGE.dataAtual();
  WebSAGE.apagaFiltro();
  WebSAGE.g_modo = 4;
  var txt = WebSAGE.g_tbl.getRowsNum() + " " + Msg.Eventos;
  var obj = document.getElementById("NUMREGS");
  if ( obj.innerHTML )
     obj.innerHTML = txt;
  else    
     obj.nextSibling.data = txt;
     
  titulmod = Msg.ModoHistorico + " - ";  
  window.onbeforeunload = null;
  }
else
  {
  // acerta posicionamento
  document.getElementById("TOOLBAR_ID").style.height = '43px';
  document.getElementById("divhead").style.top = '43px'
  document.getElementById("gridbox").style.top = '62px';
  }  

WebSAGE.g_titulo_janela = Msg.NomeVisorEventos + " - " + titulmod + Msg.NomeProduto + " - " + Msg.VersaoProduto + postitul + getUsernameFmt();
document.title = "."; // necessário devido a um bug do chromium!
document.title = WebSAGE.g_titulo_janela;

setOpacity( document.getElementById("imgNormal"), 30 );  
setOpacity( document.getElementById("imgAgregar"), 30 );  
setOpacity( document.getElementById("imgPanico"), 30 );  
setOpacity( document.getElementById("imgCongelar"), 30 );  
setOpacity( document.getElementById("imgHistorico"), 30 );

switch ( WebSAGE.g_modo )
  {
  case 0: setOpacity(document.getElementById("imgNormal"), 100); break;
  case 1: setOpacity(document.getElementById("imgAgregar"), 100); break;
  case 2: setOpacity(document.getElementById("imgPanico"), 100); break;
  case 3: setOpacity(document.getElementById("imgCongelar"), 100); break;
  case 4: setOpacity(document.getElementById("imgHistorico"), 100); break;
  }
},  

fontSize: function(updn)
{
    if ( updn == 1 && WebSAGE.g_fontsize < 30 ) 
      WebSAGE.g_fontsize = parseInt(WebSAGE.g_fontsize) + 1;
    else   
    if ( updn == 0 && WebSAGE.g_fontsize > 16 ) 
      WebSAGE.g_fontsize = parseInt(WebSAGE.g_fontsize) - 1;
    else
      return;  
    
    // escreve tamanho da fonte em cookie
    var date = new Date();
    date.setTime( date.getTime() + (3000 * 24 * 60 * 60 * 1000) );
    document.cookie = 'eve_fontsize=' + WebSAGE.g_fontsize + '; expires=' + date.toGMTString();
    
    window.onbeforeunload = null;
    
    // recarrega a página
    window.location.reload();        
},

tbl_prepare: function()
  {
    WebSAGE.g_tbl = document.getElementById('TBLEVE');
    WebSAGE.g_tblhead = document.getElementById('TBLHEAD');

    WebSAGE.g_tbl.setColAlign =
        function( vals ) 
        {  
        };

    WebSAGE.g_tbl.setInitWidths =
        function( vals ) 
        {  
        WebSAGE.g_tbl.larguraCol = [];
        var varr = vals.split(',');
        for ( var i = 0; i < varr.length; i++ )
           {
            WebSAGE.g_tbl.larguraCol[i] = varr[i];
           }
        };

    WebSAGE.g_tbl.setHeader = 
        function( vals ) 
        {  
        var varr = vals.split(',');

        if ( WebSAGE.g_tbl.rows.length == 0 )
            WebSAGE.g_tbl.insertRow( 0 );

        for ( var i = 0; i < varr.length; i++ )
        {
           WebSAGE.g_tbl.rows[0].insertCell(i);
           WebSAGE.g_tbl.rows[0].cells[i].innerHTML = varr[i];

           WebSAGE.g_tblhead.rows[0].insertCell(i);
           WebSAGE.g_tblhead.rows[0].cells[i].innerHTML = varr[i];

           if ( WebSAGE.g_tbl.larguraCol[i] == 0 )
              {
              WebSAGE.g_tbl.rows[0].cells[i].style.display = 'none';
              WebSAGE.g_tblhead.rows[0].cells[i].style.display = 'none';
              }
            WebSAGE.g_tbl.setColWidth(i, WebSAGE.g_tbl.larguraCol[i] );
        } 

        while ( WebSAGE.g_tbl.rows[0].cells.length > varr.length )
            WebSAGE.g_tbl.rows[0].deleteCell(WebSAGE.g_tbl.rows[0].cells.length - 1);

        // acerta tamanho dos campos se a fonte for maior que a padrão
        if ( WebSAGE.g_fontsize > 16 )
            {
            for ( i = 0; i < varr.length; i++ )
                WebSAGE.g_tbl.setColWidth(i,WebSAGE.g_tbl.getColWidth(i) * WebSAGE.g_fontsize / 16);
            }
        WebSAGE.g_tbl.rows[0].style.display = 'none';
        };

    WebSAGE.g_tbl.addRow = 
        function( line, vals ) 
        {  
        line++;	
        var varr = vals.split(WebSAGE.g_split_line_code);
        WebSAGE.g_tbl.insertRow( line );
        WebSAGE.g_tbl.rows[line].onclick = WebSAGE.doOnRowSelected;
                    
        for (var i=0; i < varr.length; i++)
           {
           WebSAGE.g_tbl.rows[line].insertCell(i);
           WebSAGE.g_tbl.rows[line].cells[i].innerHTML = varr[i];
           WebSAGE.g_tbl.rows[line].cells[i].style.boxSizing = "border-box";
           // WebSAGE.g_tbl.rows[line].cells[i].style.verticalAlign = "bottom";
           // WebSAGE.g_tbl.rows[line].cells[i].style.borderBottom = '1px solid ' + EventsViewer_GridColor;
           WebSAGE.g_tbl.rows[line].cells[i].style.padding = "1px 0px 1px 0px";
           if ( WebSAGE.g_tbl.larguraCol[i] == 0 )
             WebSAGE.g_tbl.rows[line].cells[i].style.display = 'none';
          else
             {
             WebSAGE.g_tbl.rows[line].cells[i].width = WebSAGE.g_tbl.larguraCol[i];  
             WebSAGE.g_tbl.rows[line].cells[i].style.minWidth = WebSAGE.g_tbl.larguraCol[i] + "px";  
             WebSAGE.g_tbl.rows[line].cells[i].style.textOverflow = "ellipsis";  
             }
           } 
        WebSAGE.g_tbl.rows[line].userData = vals;
        };

    WebSAGE.g_tbl.changeRow = 
        function( line, vals ) 
        {  
        line++;
        var varr = vals.split(WebSAGE.g_split_line_code);

        // equalizes number of columns
        while ( varr.length > WebSAGE.g_tbl.rows[line].cells.length )             
           {
           var cell = WebSAGE.g_tbl.rows[line].insertCell( -1 );
           var col = WebSAGE.g_tbl.rows[line].cells.length - 1;
           cell.style.borderBottom = '1px solid ' + EventsViewer_GridColor;
           if ( WebSAGE.g_tbl.larguraCol[col] == 0 )
             cell.style.display = 'none';
           else
             cell.width = WebSAGE.g_tbl.larguraCol[col];  
           }
         
        for ( var i = 0; i < varr.length; i++ )
           {
           WebSAGE.g_tbl.rows[line].cells[i].innerHTML = varr[i];
           } 
        WebSAGE.g_tbl.rows[line].userData = vals;
        };

    WebSAGE.g_tbl.copyRowContent = 
        function( linefrom, lineto ) 
        {
        linefrom++;	lineto++;
        for ( var i = 0; i < WebSAGE.g_tbl.rows[0].cells.length; i++ )
          {
          WebSAGE.g_tbl.rows[lineto].cells[i].innerHTML = WebSAGE.g_tbl.rows[linefrom].cells[i].innerHTML;
          } 
        WebSAGE.g_tbl.rows[lineto].userData = WebSAGE.g_tbl.rows[linefrom].userData;
        };

    WebSAGE.g_tbl.getUserData = 
        function( line ) 
        {
            line++;
            return WebSAGE.g_tbl.rows[line].userData;
        };

    WebSAGE.g_tbl.getRowsNum = 
        function( ) 
        {
            return WebSAGE.g_tbl.rows.length - 1;
        };

    WebSAGE.g_tbl.getColsNum = 
        function( ) 
        {
            return WebSAGE.g_tblhead.rows[0].cells.length;
        };

    WebSAGE.g_tbl.delRow = 
        function( row ) 
        {
            row++;
            return WebSAGE.g_tbl.deleteRow(row);
        };

    WebSAGE.g_tbl.setRowHidden = 
        function( row, hide ) 
        {
            row++;
            if ( hide )
              WebSAGE.g_tbl.rows[row].style.display = 'none';
            else  
              WebSAGE.g_tbl.rows[row].style.display = '';
        };

    WebSAGE.g_tbl.cellsx = 
        function( row, col ) 
        {
            row++;
            return WebSAGE.g_tbl.rows[row].cells[col].innerHTML;
        };      
      
    WebSAGE.g_tbl.cellsSetValue = 
        function( row, col, value ) 
        {
            row++;
            return WebSAGE.g_tbl.rows[row].cells[col].innerHTML = value;
        };      

    WebSAGE.g_tbl.getColWidth = 
        function( col ) 
        {
            return WebSAGE.g_tbl.larguraCol[col];
        };

    WebSAGE.g_tbl.setColWidth = 
        function( col, width ) 
        {
            WebSAGE.g_tbl.larguraCol[col] = width;
            try { // can cause error in IE
                  WebSAGE.g_tblhead.rows[0].cells[col].width = width;
            } catch (e) { 
            WebSAGE.g_tblhead.rows[0].cells[col].width = width + 1;
            }			
            WebSAGE.g_tblhead.rows[0].cells[col].style.minWidth = width + "px";
            WebSAGE.g_tblhead.rows[0].cells[col].style.display = (width == 0) ? 'none' : '';
            for (var i=0; i < WebSAGE.g_tbl.rows.length; i++) 
              {
                try { // can cause error in IE
                        WebSAGE.g_tbl.rows[i].cells[col].width = width;
                } catch (e) { 
                        WebSAGE.g_tbl.rows[i].cells[col].width = width + 1;
                }			
                WebSAGE.g_tbl.rows[i].cells[col].style.minWidth = width + "px";
                WebSAGE.g_tbl.rows[i].cells[col].style.display = (width == 0) ? 'none' : '';
              }
        return 0;
        };

    WebSAGE.g_tbl.setStyle = 
        function( ss_header, ss_grid, ss_selCell, ss_selRow ) 
        {
            WebSAGE.g_tbl.style.cssText = ss_grid;
            return 0;
        };

    WebSAGE.g_tbl.setRowTextStyle = 
        function( row, stl ) 
        {
            row++;
            WebSAGE.g_tbl.rows[row].style.cssText = stl;
            return 0;
        };
         
    WebSAGE.g_tbl.clearAll = 
        function( ) 
        {
            while ( WebSAGE.g_tbl.rows.length > 1 ) 
            WebSAGE.g_tbl.deleteRow(1);
        };      
  },

showInfo: function()
  {
    var wid=WebSAGE.g_tbl.getColWidth(WebSAGE.g_COL_NPONTO);
    if ( wid == 0 )
      {
      WebSAGE.g_tbl.setColWidth( WebSAGE.g_COL_NPONTO, 60 * WebSAGE.g_fontsize / 16 );
      WebSAGE.g_tbl.setColWidth( WebSAGE.g_COL_ID, 230 * WebSAGE.g_fontsize / 16 );    
      WebSAGE.g_tbl.witdh = '1200px';
      WebSAGE.g_tblhead.witdh = '1200px';
      }
    else  
      {
      WebSAGE.g_tbl.setColWidth( WebSAGE.g_COL_NPONTO, 0 );
      WebSAGE.g_tbl.setColWidth( WebSAGE.g_COL_ID, 0 );    
      WebSAGE.g_tbl.witdh = '1000px';
      WebSAGE.g_tblhead.witdh = '1000px';
      }
  },
  
// Inicializa tratamento de hora do GPS ou local para os eventos
gpsTimeInit: function( cfg )  
{
    // cfg: 0=hora do GPS, 1=hora local, 2=escolhida c/ default GPS, 3=escolhida c/ default local

    switch ( cfg )
      {
      case 0:
        WebSAGE.g_gpstime = 1;
        // se a configuração é fixa em modo de hora do GPS, nem mostra ícone
        $('#imgGpsOn').css('display', "none");
        $('#imgGpsOff').css('display', "none");
        // $('#imgGpsOn').css('opacity', "0.5");
        // $('#imgGpsOn').attr('title', Titles["imgGpsOnFx"]);
        // $('#imgGpsOn').bind('click', WebSAGE.gpsTimeOnOff);
        break;
      case 1:
        WebSAGE.g_gpstime = 0;
        $('#imgGpsOn').css('display', "none");
        $('#imgGpsOff').css('display', "");
        $('#imgGpsOff').attr('title', Titles["imgGpsOffFx"]);
        $('#imgGpsOff').css('opacity', "0.5");
        break;
      case 2:
      case 3:
        var gps = readCookie( "gpstime" );
        
        if ( isInt ( gps ) )
          WebSAGE.g_gpstime = parseInt( gps );
        else  
          WebSAGE.g_gpstime = ( cfg == 2 ) ? 1 : 0;
          
        $('#imgGpsOn').css('cursor', "pointer");
        $('#imgGpsOff').css('cursor', "pointer");
        $('#imgGpsOn').attr('title', Titles["imgGpsOnEsc"]);
        $('#imgGpsOff').attr('title', Titles["imgGpsOffEsc"]);
        $('#imgGpsOn').bind('click', WebSAGE.gpsTimeOnOff);
        $('#imgGpsOff').bind('click', WebSAGE.gpsTimeOnOff);
        $('#imgGpsOn').css('display', WebSAGE.g_gpstime ? "" : "none");
        $('#imgGpsOff').css('display',  WebSAGE.g_gpstime ? "none" : "");          
        break;
      }    
},

gpsTimeOnOff: function()
{
    if ( EventsViewer_TimeGPSorLocal == 2 || EventsViewer_TimeGPSorLocal == 3 )
      {
          WebSAGE.g_gpstime = WebSAGE.g_gpstime ? 0 : 1;
          $('#imgGpsOn').css('display', WebSAGE.g_gpstime ? "" : "none");
          $('#imgGpsOff').css('display',  WebSAGE.g_gpstime ? "none" : "");          
          
          // escreve tamanho da fonte em cookie
          var date = new Date();
               date.setTime( date.getTime() + (3000 * 24 * 60 * 60 * 1000) );
          document.cookie = 'gpstime=' + WebSAGE.g_gpstime + '; expires=' + date.toGMTString();          

 		  WebSAGE.g_tbl.setColWidth( WebSAGE.g_COL_MSEC, WebSAGE.g_gpstime * 40 );            
          WebSAGE.ModoClick();
      }
},

// get group1 (stations) list
getGroup1List : function() 
{
    // use OPC web hmi protocol https://prototyping.opcfoundation.org/
    var ServiceId = OpcServiceCode.Extended_RequestUniqueAttributeValues // read data service
    var RequestHandle = Math.floor(Math.random() * 100000000)
    var req = {
      ServiceId: ServiceId,
      Body: {
        RequestHeader: {
          Timestamp: new Date().toISOString(),
          RequestHandle: RequestHandle,
          TimeoutHint: 1500,
          ReturnDiagnostics: 2,
          AuthenticationToken: null
        },
      AttributeId: OpcAttributeId.ExtendedGroup1
      }
    }

    fetchTimeout("/Invoke/", 1500, {
      method: "POST",
      body: JSON.stringify(req),
      headers: {
        "Content-Type": "application/json"
      }
    })
      .then(function (response) {
        return response;
      })
      .then(response => response.json())
      .then(data => {
        if (!data.ServiceId || !data.Body || !data.Body.ResponseHeader || !data.Body.ResponseHeader.RequestHandle || !data.Body.Results){
          console.log("RequestUniqueAttributeValues invalid service response!");
          return;
          }

        // response must have same request handle and be a read response or service fault 
        if (data.Body.ResponseHeader.RequestHandle !== RequestHandle ||
            (data.ServiceId !== OpcServiceCode.Extended_ResponseUniqueAttributeValues && data.ServiceId !== OpcServiceCode.ServiceFault)
           ){
          console.log("RequestUniqueAttributeValues invalid or unexpected service response!");
          return;
          }
        if ( data.Body.ResponseHeader.ServiceResult !== OpcStatusCodes.Good ){
          console.log("RequestUniqueAttributeValues service error!");
          return;
        }

        data.Body.Results.map(element => {
          if (element.Value.Body !== null && element.Value.Body !== "")
            GROUP1_LIST.push(element.Value.Body);
          return element;
        });
        WebSAGE.mostraSEs();
      })
      .catch(function (error) {
        console.log(error);
      });

},

// show group1 (substation) list
mostraSEs : function() 
{
var Lista = GROUP1_LIST;
var i=0;
var htxt = '<form name="formfiltro">';

for (var se in Lista)
 {
 if ( se != undefined & Lista[i] != "" )
   htxt = htxt + '<input type="checkbox" name="chkbxfiltrose" value=' + Lista[i] + ' />' + Lista[i] + '<br />';
 i++;
 }

htxt = htxt + '</form>';

document.getElementById("DIVFILT").innerHTML = htxt;
// aumenta para caber todos as opções até um limite
if ( i > 30 )
  {
    i = 30;
  }
document.getElementById("DIVFILT").style.height = ( 20 * i ) + "px";
},

// reload page
reload: function( whattodo )
{
  if ( whattodo === "init" )
    {
    //// setup timeout to liberate browser memory
    // var dt =  new Date ( (new Date()).getTime() + 1000 * 60 * 60 * 24 ); // próximo dia
    // dt.setHours( 4 ); // fixa 4 horas
    // dt.setMinutes( Math.random() * 60 ); // minuto aleatório
    // var dif = dt - (new Date()); // quanto falta para chegar às 4:xx h do dia seguinte
    // setTimeout( WebSAGE.reload, dif ); // vai chamar às 4 h
    return;
    }

  // só recarrego se a janela de info/comando estiver fechada e for modo tempo real (não congelado ou histórico)
  if ( NPTO == 0 && WebSAGE.g_modo < 3 )
    {  
    window.location.href = window.location.origin + window.location.pathname + "?MODO=" + WebSAGE.g_modo;
    }
  else
    { // se estiver ocupado, adia por 1 hora
    setTimeout( WebSAGE.reload, 60 * 60 * 1000 );
    }    
},

init: function()
  {
    WebSAGE.g_loadtime = new Date();

    WebSAGE.tbl_prepare();

    // vai nos objetos com 'id' e coloca como 'title' a mensagem correspondente de Titles, carrega as imagens (de images.js)        
    $('img[id]').attr('src', function(index) { return Imgs[this.id]; });
    $('img[id]').attr('title', function(index) { return Titles[this.id]; });
    $('span[id]').attr('title', function(index) { return Titles[this.id]; });
    $('input[id]').attr('title', function(index) { return Titles[this.id]; });
    $('#HIST').text( Msg.HIST );
    $('#SPDATAINI').text( Msg.SPDATAINI );
    $('#SPHORAINI').text( Msg.SPHORAINI );
    $('#SPFILTRO').text( Msg.SPFILTRO );
    $('#btBuscaHist').text( Msg.btBuscaHist );
    $('#TOOLBAR_ID').css('background-color', EventsViewer_ToolbarColor);
    if ( EventsViewer_AllowFilter )
       $('#imgFilter').css('display', "" );
    $('#imgFilter').bind( 'click', function(){ if (! document.getElementById("idHistorico").checked ) $('#DIVFILT').css('display', $('#DIVFILT').css('display')=="none" ? "" : "none"); } );

    $('#TOOLBAR_ID').css('display', ''); // show toolbar
    
    WebSAGE.gpsTimeInit( EventsViewer_TimeGPSorLocal );

    LoadFavicon( Imgs.FAVICONEVENTOS );

    WebSAGE.g_tbl.setColAlign("center,center,center,center,left,center,left,center,left,center");

    // looks for font size on cookie
    var fs = readCookie( "eve_fontsize" );
    if ( isInt ( fs ) )
      WebSAGE.g_fontsize = parseInt( fs );
    
    if ( WebSAGE.g_fontsize <= 16 )
      document.getElementById("imgFontSizeDown").style.display = "none";
    if ( WebSAGE.g_fontsize >= 30 )
      document.getElementById("imgFontSizeUp").style.display = "none";

    // acerta a posição do box de filtros para ficar ao lado do ícone do funil
    $('#DIVFILT').css( 'left', document.getElementById('imgFilter').getBoundingClientRect().right + 5 );

    var gridstyle = "border-collapse:collapse;outline:none;cursor:pointer;font-weight:normal;border:0px;white;padding:0px;margin-left:5px;";
    gridstyle = gridstyle + 'table-layout:fixed;white-space:nowrap;overflow:hidden;';
    gridstyle = gridstyle + 'color:' + EventsViewer_AlmTxtColor + ';'; 
    gridstyle = gridstyle + 'font-family:' + EventsViewer_Font + ';'; 
    WebSAGE.g_tbl.setStyle(';', 'height:auto;font-size:' + WebSAGE.g_fontsize + 'px;' + gridstyle,';',';');
    WebSAGE.g_tbl.setInitWidths("80,80,40,0,0,70,400,170,0,55,0,0");

    WebSAGE.g_tbl.setHeader(Msg.EveNomesColunas);

    $('#gridbox').css('background-color', EventsViewer_TableColor);
    WebSAGE.g_tbl.cellSpacing = "0px";
    WebSAGE.g_tbl.cellPadding = "0px";
    WebSAGE.g_tblhead.cellSpacing = "0px";
    WebSAGE.g_tblhead.cellSpacing = "0px";
    WebSAGE.g_tblhead.cellPadding = "0px";
    $('#TBLHEAD').css('overflow', 'hidden');
    $('#TBLHEAD').css('white-space', 'nowrap');
    $('#TBLHEAD').css('margin-left', '5px');
    $('#TBLHEAD').css('table-layout', 'fixed');
    $('#TBLHEAD').css('background-color', 'gainsboro');
    document.body.bgColor = 'gainsboro';
    $('#TBLHEAD').css('color', 'black');
    $('#TBLHEAD').css('font-family', EventsViewer_Font);
    
    WebSAGE.g_tbl.setColWidth( WebSAGE.g_COL_MSEC, WebSAGE.g_gpstime * 40 );            
      
    // começa a atualizar     
    WebSAGE.g_toutID = setTimeout( WebSAGE.callServer, 2000 );
  
    WebSAGE.dataAtual();
    
    shortcut.add( "F2", // delete all events
                  function() { WebSAGE.doReconheceTudo(1); },
                  { 'type':'keydown', 'propagate':true,	'target':document } );
    shortcut.add( "F8", // ack all events
                  function() { WebSAGE.doReconheceTudo(0); },
                  { 'type':'keydown', 'propagate':true,	'target':document } );
    shortcut.add( "F9", // silence beep
                  function() { WebSAGE.doSilenceBeep(); },
                  { 'type':'keydown', 'propagate':true,	'target':document } );
    shortcut.add( "1",
                  function() { document.getElementById("idNormal").click(); },
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    shortcut.add( "2",
                  function() { document.getElementById("idAgregar").click(); },
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    shortcut.add( "3",
                  function() { document.getElementById("idPanico").click();  },
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    shortcut.add( "4",
                  function() { document.getElementById("idCongelar").click(); },
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    shortcut.add( "5",
                  function() { document.getElementById("idHistorico").click(); },
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    shortcut.add( "6",
                  function() { WebSAGE.dataAtual(); WebSAGE.zeraHoraInicial(); },
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    shortcut.add( "7",
                  function() { WebSAGE.zeraHoraInicial(); },  
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    shortcut.add( "8",
                  function() { WebSAGE.apagaFiltro(); },
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    shortcut.add( "0",
                  function() { WebSAGE.doOnRowSelected(0); },
                  { 'type':'keydown', 'propagate':true,	'disable_in_input':true, 'target':document } );
    // font size keyboard shortcuts
    shortcut.add( "", // Num[+] : Bigger font size
                  function() { WebSAGE.fontSize(1); },
                  { 'type':'keydown', 'propagate':false,	'target':document, 'keycode':107 } );
    shortcut.add( "", // Num[-] : Smaller font size
                  function() { WebSAGE.fontSize(0); },
                  { 'type':'keydown', 'propagate':false,	'target':document, 'keycode':109 } );

    // atalhos para percorrer a lista de pontos
    shortcut.add( "home", 
                  function() { document.getElementById('TBLEVE').focus(); },
                  {'type':'keydown', 'propagate':true,	'target':document} );
    shortcut.add( "end", 
                  function() { document.getElementById('TBLEVE').focus(); },
                  {'type':'keydown', 'propagate':true,	'target':document} );    
    shortcut.add( "pagedown", 
                  function() { document.getElementById('TBLEVE').focus(); },
                  {'type':'keydown', 'propagate':true,	'target':document} );
    shortcut.add( "pageup", 
                  function() { document.getElementById('TBLEVE').focus(); },
                  {'type':'keydown', 'propagate':true,	'target':document} );    
    shortcut.add( "down", // desce sem numlock
                  function() { document.getElementById('TBLEVE').focus(); },
                  {'type':'keydown', 'propagate':true,	'target':document} );
    shortcut.add( "up", // sobe sem numlock
                  function() { document.getElementById('TBLEVE').focus(); },
                  {'type':'keydown', 'propagate':true,	'target':document} );

    shortcut.add( "esc",
                  function() { if ( typeof(WebSAGE.g_win_cmd.window) == 'object' ) // close faceplate info window
                               if ( WebSAGE.g_win_cmd.window ) 
                                 WebSAGE.g_win_cmd.window.close(); },
                  {'type':'keydown', 'propagate':false, 'target':document} );           

    // dispara ventoinha
    Ventoinha.init("VENTOINHA"); 
    Ventoinha.periodico();
    
    setOpacity(document.getElementById("imgAgregar"), 30);  
    setOpacity(document.getElementById("imgPanico"), 30);  
    setOpacity(document.getElementById("imgCongelar"), 30);  
    setOpacity(document.getElementById("imgHistorico"), 30);
    
    WebSAGE.g_titulo_janela = Msg.NomeVisorEventos + " - " + Msg.NomeProduto + " - " + Msg.VersaoProduto + " - " + Msg.ModoNormal + getUsernameFmt();
    document.title = "."; // necessário devido a um bug do chromium!
    document.title = WebSAGE.g_titulo_janela;

    // testa se realmente deseja sair
    window.onbeforeunload = function() { return Msg.ConfirmaSaida; };
    
    var modo = gup("MODO");
    if ( modo == 4 )
      {
	  document.getElementById("idHistorico").click();
	  }
    
    WebSAGE.reload( "init" );
      
    document.getElementById('TBLEVE').tabIndex = 1; // permite foco na tabela
    document.getElementById('TBLEVE').focus();      
       
    // desabilita o botão direito 
    document.oncontextmenu = function() { return false; };    
    
    setTimeout( WebSAGE.getGroup1List, 500 );
  } // init
}; // WebSAGE

Core.start(WebSAGE);  // vai chamar metodo init() somente quando terminar de carregar a página

//set the opacity of an element to a specified value
function setOpacity(obj, o) {
    obj.style.opacity = (o / 100);
    obj.style.MozOpacity = (o / 100);
    obj.style.KhtmlOpacity = (o / 100);
    obj.style.filter = 'alpha(opacity=' + o + ')';
}

</script>

</head>

<body style="margin:0px; overflow:hidden; user-select:none;">
<div id='DIV_TOOL' style='position:fixed;top:0px;left:0px;width:100%;z-index:1;box-shadow: 2px 2px 2px #999999;'>
<table id="TOOLBAR_ID" style='display:none;font-family:tahoma; font-size:16px;' height='43px' width='100%' cellpadding='0' cellspacing='0'>
<tr>
<td style='vertical-align:middle;white-space:nowrap;'>&nbsp;
<img id='IMGEVENTOS' alt='' src='' align='middle' width='32' height='32' onclick='WebSAGE.showInfo();' />
<img id='imgAlarmes' alt='' src='' style='display:none;' align='middle' border='0' width='32' height='32' onclick='WebSAGE.showInfo();' />
&nbsp;&nbsp;&nbsp;&nbsp;
<img id='IMGSEPAR1' alt='' src='' align='middle' width='8' height='32' style='display:none;' />
<input id='idNormal' style='display:none;vertical-align:middle' type="radio" name="grpModo" onclick='WebSAGE.ModoClick();' value="vlNormal" checked><img id='imgNormal' alt='' src='' style='cursor:pointer;border-bottom:1px solid;' align='middle' width='32' height='32' onMouseover='setOpacity(this, 100);' onMouseout='if(WebSAGE.g_modo!=0) setOpacity(this, 30)' onclick='if(WebSAGE.g_modo!=0) document.getElementById("idNormal").click()' ><input style='display:none;vertical-align:middle' type="radio" id="idAgregar"   name="grpModo" onclick='WebSAGE.ModoClick();' value="vlAgregar"><img id='imgAgregar' alt='' src='' style='cursor:pointer;border-bottom:1px solid;' align='middle' width='32' height='32' onMouseover='setOpacity(this, 100);' onMouseout='if(WebSAGE.g_modo!=1) setOpacity(this, 30)' onclick='if(WebSAGE.g_modo!=1) document.getElementById("idAgregar").click()'><input style='display:none;vertical-align:middle' type="radio" id="idPanico"    name="grpModo" onclick='WebSAGE.ModoClick();' value="vlPanico"><img id='imgPanico' alt='' src='' style='cursor:pointer;border-bottom:1px solid;' align='middle' width='32' height='32' onMouseover='setOpacity(this, 100)' onMouseout='if(WebSAGE.g_modo!=2) setOpacity(this, 30)' onclick='if(WebSAGE.g_modo!=2) document.getElementById("idPanico").click()' ><input style='display:none;vertical-align:middle' type="radio" id="idCongelar"  name="grpModo" onclick='WebSAGE.ModoClick();' value="vlCongelar"><img id='imgCongelar' alt='' src='' style='cursor:pointer;border-bottom:1px solid;' align='middle' width='32' height='32' onMouseover='setOpacity(this, 100)' onMouseout='if(WebSAGE.g_modo!=3) setOpacity(this, 30)' onclick='if(WebSAGE.g_modo!=3) document.getElementById("idCongelar").click()' ><input style='display:none;vertical-align:middle' type="radio" id="idHistorico" name="grpModo" onclick='WebSAGE.ModoClick();' value="vlHistorico"><img id='imgHistorico' alt='' src='' style='cursor:pointer;border-bottom:1px solid;' align='middle' width='32' height='32' onMouseover='setOpacity(this, 100)' onMouseout='if(WebSAGE.g_modo!=4) setOpacity(this, 30)' onclick='if(WebSAGE.g_modo!=4) document.getElementById("idHistorico").click()' >
<img id='IMGSEPAR2' alt='' src='' align='middle' width='8' height='32' style='display:none;' />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<img id='imgGpsOn' alt='' src='' align='middle' width="27" height="27" style='display:none;' />
<img id='imgGpsOff' alt='' src='' align='middle' width="27" height="27" style='display:none;' />
&thinsp;
<img id='imgFontSizeUp' alt='' src='' align='middle' width="27" height="27" onclick='WebSAGE.fontSize(1)' style='cursor:pointer;' />
<img id='imgFontSizeDown' alt='' src='' align='middle' width="27" height="27" onclick='WebSAGE.fontSize(0)' style='cursor:pointer;' />
&thinsp;
<img id='imgClipboard' alt='' src='' onclick='CopyClipboard("TBLEVE")' align='middle' border='0' width='32' height='32' style='cursor:pointer;' />
&thinsp;
<img id='imgFilter' alt='' src='' align='middle' border='0' width='32' height='32' style='cursor:pointer; display:none;' />
&thinsp;
<img id='imgReconheceTudo' alt='' src='' align='middle' width="32" height="32" onclick='WebSAGE.doReconheceTudo(0);' style='cursor:pointer;' />
&thinsp;
<img id='imgEliminaTudo' alt='' src='' align='middle' width="32" height="32" onclick='WebSAGE.doReconheceTudo(1);' style='cursor:pointer;' />
&thinsp;
<img id='SILENCIA_ID' alt='' src='' align='middle' width="32" height="32" onclick='WebSAGE.doSilenceBeep();' style='display:none; cursor:pointer;' />
<img id='IMGSEPAR3' alt='' src='' align='middle' width='8' height='32' style='display:none;' />
<div id='DIVFILT' style="display:none; position:absolute; top:0px; left:650px; width:120px; height:80px; overflow:auto; font-family:arial; font-size:12pt; background-color:lightgray; vertical-align:middle; border-radius:5px; -moz-border-radius:5px; padding:0px 5px; border:2px solid black">
</div>
&nbsp;
<small><span id='NUMREGS'>?</span></small>

<div style="position: absolute; right: 0px; top: 0px; ">
<span id='VENTOINHA' style='font-family:courier; font-weight:bold;'>(!)</span>
</div>

</td>
</tr>
<tr id="HISTCTRLS" style="display:none;">
<td style='vertical-align:middle;white-space:nowrap;' >
<div style="text-shadow: 0 0 0.2em gray">
<span id="HIST">?</span>
<datalist id="specialtimes">
  <option>00:00:00</option>
  <option>04:00:00</option>
  <option>08:00:00</option>
  <option>10:00:00</option>
  <option>12:00:00</option>
  <option>14:00:00</option>
  <option>16:00:00</option>
  <option>18:00:00</option>
  <option>20:00:00</option>
  <option>22:00:00</option>
  </datalist>  
&nbsp;&nbsp;<span id="SPDATAINI">?</span><input id='HDATAINI' type='date' size='12' style='text-shadow: 1px 1px 1px #808080;' />&nbsp;
&nbsp;&nbsp;<span id="SPHORAINI">?</span><input id='HHORAINI' list='specialtimes' type='time' step='1' value='00:00:00' size='12' style='text-shadow: 1px 1px 1px #808080;' />&nbsp;
&nbsp;&nbsp;<span id="SPFILTRO">?</span><input  id='HFILTRO' size='12' style='text-shadow: 1px 1px 1px #808080;' />&nbsp;
&nbsp;&nbsp;<button id="btBuscaHist" type="button" onclick='WebSAGE.histBusca();'>?</button>
</div>
</td>
</tr>
</table>
</div>
<div id="divhead" style="position:fixed;top:43px;width:100%;height:18px;">
<table id="TBLHEAD">
<tr><td></td></tr>
</table>
</div>
<div id="gridbox" style="position:fixed;top:62px;width:100%;height:calc(100% - 43px - 18px);overflow-y:auto;">
<table id="TBLEVE">
</table>
</div>
</body>
</html>
